package it.test.thread.threadPool;

import java.io.Serializable;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * {@code @Copyright} 纳新智能科技有限公司
 * {@code @Author} lky
 * {@code @Date} 2023/8/15
 */
public class MyThreadPool {

    /**
     * 过期时间，与构造方法中的timeUnit结合使用，作用是从阻塞队列中poll时如果未返回数据时阻塞的超时时间
     * keepAliveTime与timeUnit会转化为纳秒供阻塞队列使用，由于多个工作线程需要使用它判断状态所以需要用volatile来修饰
     */
    private volatile long keepAliveTime;

    /**
     * 线程工厂，在内部类worker中包含两个参数，一个thread线程参数用于执行任务，一个runnable任务参数用于表示待处理的任务
     * 其中线程工厂就是为了给worker创建线程用于执行任务，由于多个工作线程都需要它来创建所以需要用volatile来修饰
     */
    private volatile ThreadFactory threadFactory;

    /**
     * 阻塞队列，核心参数，所有待处理的任务都会加入阻塞队列，遵守先进先出原则
     * BlockingQueue是一个接口，在创建线程池时需要将阻塞队列的类型换成具体的实现类，如LinkedBlockingQueue、DelayQueue等
     */
    private final BlockingQueue<Runnable> blockingQueue;

    /**
     * 核心可重入锁，线程池内很多方法都需要加锁，因为线程池是一个可以被多个线程访问的共享资源
     * 为了保证线程池状态、任务等资源的一致性，所以添加任务、修改线程池状态等方法都需要在加锁的情况下执行，防止多个线程操作同一个任务导致的问题
     */
    private final ReentrantLock mainLock = new ReentrantLock();

    /**
     * 核心线程数是线程池中保持活跃的最小线程数,这些线程会在任务添加进来后立即去执行
     * 核心线程执行结束后也不会销毁,会继续保留等待后续任务的处理,这样可以有效减少创建与销毁线程的内存开销
     */
    private volatile int corePoolSize;

    /**
     * 最大线程数是线程池中最多可运行的工作线程数,超过最大线程数就表示任务已经饱和,需要去执行拒绝策略的逻辑
     */
    private volatile int maximumPoolSize;




    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }
    private static boolean runStateLessThan(int c, int s) {
        return c < s;
    }
    private static boolean runStateAtLeast(int c, int s) {return c >= s;}
    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }

    public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit timeUnit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.blockingQueue = workQueue;
        this.keepAliveTime = timeUnit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
    }

    public ThreadFactory getThreadFactory() {
        return threadFactory;
    }

    public void setThreadFactory(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
    }

    /**
     * 线程池的核心执行任务方法，调用链路为execute-addWorker-runTask
     * @param task 待执行的多线程任务
     */
    private boolean execute(Runnable task){
        //如果没有执行任务直接报空指针
        if (task == null) {
            throw new NullPointerException();
        }
        return true;
    }

    /**
     * 添加工作线程的方法
     * @return 是否添加成功
     */
    private boolean addWorker(){
        return true;
    }

    /**
     * 线程池的真正核心执行任务方法，工作线程worker的run方法即为调用该方法
     * 问题1：如果每有一个新任务就直接创建一个新线程去处理，那可能多次创建线程会消耗大量的内存
     * 问题2：线程池创建新线程时需要用到mainLock这个可重入锁，直接创建工作线程去处理任务会影响效率
     * 为了处理问题1和2需要使用阻塞队列，阻塞队列在队列任务已满的情况下插入数据会阻塞，没有队列任务时poll也会进行超时阻塞
     *
     * 为了防止工作线程任务完成后就会终止，需要将工作线程设置为死循环，所以使用while(true)来作为循环条件
     * 为了可以让多个线程都从一个地方取出任务来执行，需要将任务放在一个内存共享区域，所以用final来修饰阻塞队列
     * @param worker 内部类工作线程
     */
    private void runWorker(Worker worker){
        //获取当前线程，用于beforeExecute执行前预处理一些逻辑
        Thread wt = Thread.currentThread();
        //获取工作线程的待处理任务
        Runnable task = worker.firstTask;
        //将工作线程的待处理任务置空，表明该任务已被处理
        worker.firstTask = null;
        //不断循环从阻塞队列中poll任务来执行，如果无法获取到任务则说明任务全部结束，然后就可以跳出while循环
        try {
            while ((task!=null) || ((task=getTask())!=null)){
                //对工作线程加锁
                worker.lock();
                //执行前首先要判断当前线程是否需要关闭,以下情况需要将当前线程中断
                //1.线程池已经是STOP状态
                //2.线程池已经是STOP状态但是当前线程尚未中断
                //在执行前主动检查一遍工作线程的状态,可以让其更好的结束任务,释放CPU资源
                if ((runStateAtLeast(ctl.get(), STOP) ||
                        (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) {
                    wt.interrupt();
                }
                try {
                    //beforeExecute与afterExecute为运行任务完成前后的预处理方法,本身没有任何代码,但可以通过子类来实现自己的逻辑
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        //任务开始执行
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    //将task置空表示该任务执行完毕
                    task = null;
                    //对工作线程解锁
                    worker.unlock();
                }
            }
        } finally {
            System.out.println("开始执行销毁工作线程");
        }
    }

    /**
     * 获取队列任务的方法，在队列中有任务的情况下任务线程会一直处于while(true)，所以工作线程不会中止
     * @return 阻塞队列头部的任务
     */
    private Runnable getTask(){
        try {
            Runnable task = blockingQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
            return task;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 工作线程运行任务前的处理方法
     * @param wt 处理任务的工作线程
     * @param task 待处理的任务
     */
    private void beforeExecute(Thread wt, Runnable task) { }

    /**
     * 任务执行完成后的调用方法，此方法由执行任务的线程调用
     * @param task 待处理的任务
     * @param thrown 可能导致线程中断的异常，如果未抛出异常即为null
     */
    private void afterExecute(Runnable task, Throwable thrown) { }























    /**
     * 内部工作类Worker，真正用于处理线程池任务的类
     * Worker需要实现AbstractQueuedSynchronizer即AQS,此处用lock与unlock表示实现加锁解锁
     */
    private final class Worker implements Runnable, Serializable {
        private static final long serialVersionUID = 6138294804551838833L;
        /**
         * 执行任务的线程
         */
        final Thread thread;
        /**
         * 初始化时设置的待执行任务，可能为null
         */
        Runnable firstTask;

        Worker(Runnable firstTask) {
            this.thread = getThreadFactory().newThread(this);
            this.firstTask = firstTask;
        }

        private void lock(){
            System.out.println("当前线程已加锁!");
        }
        private void unlock(){
            System.out.println("当前线程已解锁!");
        }

        /**
         * 实际运行方法为调用主线程的runWorker方法
         */
        @Override
        public void run() {
            runWorker(this);
        }
    }
}
